package com.newland.rbac.config.shiro;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.newland.rbac.config.jwt.JwtProperties;
import com.newland.rbac.config.jwt.JwtTokenUtil;
import com.newland.rbac.model.User;
import com.newland.rbac.service.UserService;
import com.newland.utils.CustomException;
import com.newland.utils.CustomExceptionType;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.concurrent.TimeUnit;

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 大坑，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof CustomToken;
    }


    /**
     * 授权相关
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        String token = (String) principalCollection.getPrimaryPrincipal();
        String principal = jwtTokenUtil.getPrincipalFromToken(token);
        List<String> roles = userService.findRolesByPrincipal(principal);
        simpleAuthorizationInfo.addRoles(roles);
        List<String> apis = userService.findApisByPrincipal(principal);
        simpleAuthorizationInfo.addStringPermissions(apis);
        return simpleAuthorizationInfo;
    }

    /**
     * 认证身份
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String token = "";
        token = (String) authenticationToken.getPrincipal();
        String oldToken=token;
        String principal = "";
        try {
            principal = jwtTokenUtil.getPrincipalFromToken(token);
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(CustomExceptionType.USER_INPUT_ERROR,e.getMessage());
        }
        if(jwtTokenUtil.isTokenExpired(token)){
            if(stringRedisTemplate.opsForValue().get(principal)==null){
                return null;
            }
            oldToken=token;
            long currentTimeMillis = System.currentTimeMillis();
            try {
                token = jwtTokenUtil.refreshToken(token, currentTimeMillis);
            } catch (Exception e) {
                e.printStackTrace();
                throw new CustomException(CustomExceptionType.SYSTEM_ERROR,e.getMessage());
            }
            stringRedisTemplate.opsForValue().set(principal,String.valueOf(currentTimeMillis),jwtProperties.getRedisexpiration(), TimeUnit.MILLISECONDS);
        }
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("name",principal);
        User user = userService.getOne(userQueryWrapper);
        if(user==null){
            throw new CustomException(CustomExceptionType.USER_INPUT_ERROR,"未知用户");
        }
        if(jwtTokenUtil.validateToken(token)){
            return new SimpleAuthenticationInfo(oldToken,oldToken,getName());//这里一旦刷新了token，如果不更改就会拿老token和新token对比，自然不成功，要么重写底层方法，要么形式判断一下，真正不用它做判断，而是自己判断了的，只要它返回成功信号而已
        }else{
            return null;//认证不通过请重新登录
        }
    }
}
